package com.github.gaojh.aop;

import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;


/**
 * @author gaojh
 * @since 2020/3/28
 */
@Data
@Slf4j
public class AopExecution {

    //可空
    private String modifierPattern;
    //不可控
    private String retTypePattern;
    //可空
    private String declaringTypePattern;
    //不可空
    private String namePattern;
    //不可空
    private String paramPattern;
    //可空
    private String throwsPattern;

    public void setExecution(String execution) {
        if (StrUtil.isBlank(execution)) {
            log.error("AOP表达式为空");
            return;
        }
        String[] spExecution = StrUtil.split(execution, " ");
        if (spExecution.length == 2) {
            this.retTypePattern = spExecution[0];
            this.retTypePattern = StrUtil.replace(this.retTypePattern, "*", "([a-zA-Z]|.)*");
            String pkgAndNameAndParam = spExecution[1];

            setNameAndParam(pkgAndNameAndParam);
        } else if (spExecution.length == 3) {
            if (StrUtil.equalsAnyIgnoreCase(spExecution[0], "public", "private", "protected", "default")) {
                this.modifierPattern = spExecution[0];
                this.retTypePattern = spExecution[1];
                this.retTypePattern = StrUtil.replace(this.retTypePattern, "*", "([a-zA-Z]|.)*");
                String pkgAndNameAndParam = spExecution[2];
                setNameAndParam(pkgAndNameAndParam);
            } else {
                this.retTypePattern = spExecution[0];
                this.retTypePattern = StrUtil.replace(this.retTypePattern, "*", "([a-zA-Z]|.)*");
                String pkgAndNameAndParam = spExecution[1];
                setNameAndParam(pkgAndNameAndParam);
                this.throwsPattern = spExecution[2];
            }
        } else if (spExecution.length == 4) {
            this.modifierPattern = spExecution[0];
            this.retTypePattern = spExecution[1];
            this.retTypePattern = StrUtil.replace(this.retTypePattern, "*", "([a-zA-Z]|.)*");
            String pkgAndNameAndParam = spExecution[2];
            setNameAndParam(pkgAndNameAndParam);
            this.throwsPattern = spExecution[3];
        } else {
            log.error("AOP表达式错误：{}", execution);
        }
    }

    private void setNameAndParam(String pkgAndNameAndParam) {
        if (!StrUtil.contains(pkgAndNameAndParam, "(") && !StrUtil.contains(pkgAndNameAndParam, ")")) {
            log.error("AOP表达式错误，需要包含()");
            return;
        }

        this.paramPattern = StrUtil.subBetween(pkgAndNameAndParam, "(", ")");
        if(StrUtil.isNotBlank(this.paramPattern)){
            if(StrUtil.equals(this.getParamPattern(),"..")) {
                this.paramPattern = StrUtil.replace(this.paramPattern, "..", "([a-zA-Z]|.|,)*");
            }else {
                this.paramPattern = StrUtil.replace(this.paramPattern, "*", "([a-zA-Z]|%)*");
                this.paramPattern = StrUtil.replace(this.paramPattern, ".", "\\.");
                this.paramPattern = StrUtil.replace(this.paramPattern, "%", ".");
            }
        }
        String pkgAndName = StrUtil.subBefore(pkgAndNameAndParam, "(", true);
        if (StrUtil.contains(pkgAndName, '.')) {
            this.namePattern = StrUtil.subAfter(pkgAndName, ".", true);
            this.declaringTypePattern = StrUtil.subBefore(pkgAndName, ".", true);
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, "..*", "\\%([a-zA-Z]|%)#\\%");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, "..", "\\%([a-zA-Z]|%)#\\%");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, ".*.", "\\%([a-zA-Z])#\\%");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, ".*", "\\%([a-zA-Z])#");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, "*.", "([a-zA-Z])#\\%");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, "#", "*");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, ".", "\\.");
            this.declaringTypePattern = StrUtil.replace(this.declaringTypePattern, "%", ".");

        } else {
            this.namePattern = pkgAndName;
        }
        this.namePattern = StrUtil.replace(this.namePattern, "*", "[a-zA-Z]*");
    }
}
